
package cn.wjdiankong.levideo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import cn.wjdiankong.levideo.R;

/**
 * 彩色三个点的Loading控件
 * 
 * @author LDM
 * @version [CleanMaster, 2014-9-22]
 */
public class ColorPointMoveLoadingView extends View {

    // 每次增加的大小
    private final float DEFAULT_SCALE_FACTOR = 0.3f;

    // 每次移动的因子大小
    private final int DEFAULT_MOVE_FACTOR = 1;
    
	private float mScaleFactor = DEFAULT_SCALE_FACTOR;
	
	private int mMoveFactor = DEFAULT_MOVE_FACTOR;

    private ColorPoint[] colorPoints;

    private boolean animateSwitch = false;

    // 每次动画隔着每一帧时间
    private int intervalTime = 20;

    // 最小半径
    private float commonRadius = 14;
    
    private float maxRadius = 40;

    // 行走路程
    private int distance = 100;
    
    /** 速度 （0-1之间），不能为0 */
    private float mSpeed = 1.0f;

    public ColorPointMoveLoadingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray array = context.obtainStyledAttributes(attrs,
                R.styleable.ColorPointMoveLoadingView);
        init(array);
        array.recycle();
    }

    public ColorPointMoveLoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray array = context.obtainStyledAttributes(attrs,
                R.styleable.ColorPointMoveLoadingView);
        init(array);
        array.recycle();
    }

    public ColorPointMoveLoadingView(Context context) {
        super(context);
        init(null);
    }

	private void init(TypedArray array) {
		if (null != array) {
			// 如果有外部设置属性, 就设置一下
			//commonRadius = array.getDimension(R.styleable.ColorPointMoveLoadingView_common_radius, commonRadius);
			intervalTime = array.getInteger(R.styleable.ColorPointMoveLoadingView_interval, intervalTime);
			distance = array.getInteger(R.styleable.ColorPointMoveLoadingView_distance, distance);
			mSpeed = array.getFloat(R.styleable.ColorPointMoveLoadingView_speeds, mSpeed);
		}
		intervalTime *= getSpeedScale();
		mScaleFactor = DEFAULT_SCALE_FACTOR * getSpeedScale();
		mMoveFactor = DEFAULT_MOVE_FACTOR * getSpeedScale();
        maxRadius = calMaxRadium(commonRadius);
        
        Paint orangePaint = new Paint();
        orangePaint.setColor(getResources().getColor(R.color.cm_color_point_loading_orange));
        orangePaint.setAntiAlias(true);

        Paint greenPaint = new Paint();
        greenPaint.setColor(getResources().getColor(R.color.cm_color_point_loading_green));
        greenPaint.setAntiAlias(true);

        Paint bluePaint = new Paint();
        bluePaint.setColor(getResources().getColor(R.color.cm_color_point_loading_blue));
        bluePaint.setAntiAlias(true);

        colorPoints = new ColorPoint[3];
        colorPoints[0] = new ColorPoint(orangePaint, commonRadius, 255, (int) distance, 0);
        colorPoints[1] = new ColorPoint(greenPaint, commonRadius, 255, (int)
                distance, distance / 3);
        colorPoints[2] = new ColorPoint(bluePaint, commonRadius, 255, (int)
                distance, 2 * distance / 3);

        if(getVisibility() == View.VISIBLE) {
            startAnimation();
        }
    }
    
    private float calMaxRadium(float commonRadius) {
        float rad = commonRadius;
		for (int i = 0; i < distance;) {
			i += mMoveFactor;
			rad += mScaleFactor;
		}
        return rad;
    }
    
    private int getSpeedScale() {
    	return (int) (1.0f / mSpeed);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        canvas.translate(maxRadius, 0);

        colorPoints[0].setOffsetTime(colorPoints[0].getOffsetTime() + mMoveFactor);
        colorPoints[1].setOffsetTime(colorPoints[1].getOffsetTime() + mMoveFactor);
        colorPoints[2].setOffsetTime(colorPoints[2].getOffsetTime() + mMoveFactor);

        for (ColorPoint cp : colorPoints) {
            canvas.drawCircle(cp.getMoveOffset(), (getMeasuredHeight()) / 2,
                    cp.getRadius(),
                    cp.getPaint());
        }

    }

    public interface OnApplyTransformationListener {
        void applyTransformation();
    }

    OnApplyTransformationListener mOnApplyTransformationListener;

    public void setOnApplyTransformationListener(OnApplyTransformationListener listener) {
        mOnApplyTransformationListener = listener;
    }
    
    /**
     * 开始启动动画
     * 
     * @author LDM
     */
    public void startAnimation() {
        if (!animateSwitch) {
            animateSwitch = true;

            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (animateSwitch) {
                        try {
                            Thread.sleep(intervalTime);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        if (mOnApplyTransformationListener != null) {
                            mOnApplyTransformationListener.applyTransformation();
                        }
                        // 解决三星W999中，其定制系统导致修改源代码，没对mAttchInfo进行判空的bug
                        if (getWindowToken() != null) {
                            postInvalidate();
                        }
                    }
                }
            }).start();
        }
    }

    /**
     * 停止动画播放
     * 
     * @author LDM
     */
    public void stopAnimation() {
        if (animateSwitch) {
            // 复位
            animateSwitch = false;
        }
    }

    /**
     * 是否在播放动画
     * 
     * @return
     * @author LDM
     */
    public boolean isActived() {
        return animateSwitch;
    }

    public int getIntervalTime() {
        return intervalTime;
    }

    public void setIntervalTime(int intervalTime) {
        this.intervalTime = intervalTime;
    }

    public float getCommonRadius() {
        return commonRadius;
    }

    public void setCommonRadius(float commonRadius) {
        this.commonRadius = commonRadius;
        colorPoints[0].setRadius(commonRadius);
        colorPoints[1].setRadius(commonRadius);
        colorPoints[2].setRadius(commonRadius);
    }

    public int getDistance() {
        return distance;
    }

    public void setDistance(int distance) {
        this.distance = distance;
        colorPoints[0].setTotalTime(distance);
        colorPoints[0].setOffsetTime(0);

        colorPoints[1].setTotalTime(distance);
        colorPoints[1].setOffsetTime(distance / 3);

        colorPoints[2].setTotalTime(distance);
        colorPoints[2].setOffsetTime(2 * distance / 3);
    }
    
	public void setSpeed(float speed) {
		if (speed <= 1.0f && speed > 0.0f) {
			this.mSpeed = speed;
		} else {
			throw new IllegalArgumentException("ColorPointMoveLoadingView speed must be (0.0f, 1.0f]");
		}
	}

    class ColorPoint {

        private Paint paint;

        private float radius;

        private int alpha;

        private int moveOffset;

        private int totalTime;

        private int offsetTime;

        private float defaultRadius;

        public ColorPoint(Paint paint, float radius, int alpha, int totalTime, int startTime) {
            super();
            this.paint = paint;
            this.radius = radius;
            this.defaultRadius = radius;
            this.alpha = alpha;
            this.totalTime = totalTime;
            this.offsetTime = startTime;
            // initRadius(startTime);
        }

        public Paint getPaint() {
            return paint;
        }

        public void setPaint(Paint paint) {
            this.paint = paint;
        }

        public float getRadius() {
            return radius;
        }

        public void setRadius(float radius) {
            this.radius = radius;
        }

        public int getAlpha() {
            return alpha;
        }

        public void setAlpha(int alpha) {
            this.alpha = alpha;
        }

        private double getCal() {
            return Math.sin(Math.PI * (double) offsetTime / (double) totalTime);
        }

        public int getMoveOffset() {
            double cal = getCal();
            if (cal < 0) {
                cal = 0;
            }
            // 计算出偏移量
            moveOffset = (int) (cal * offsetTime);
            return moveOffset;
        }

        public void setMoveOffset(int moveOffset) {
            this.moveOffset = moveOffset;
        }

        public int getTotalTime() {
            return totalTime;
        }

        public void setTotalTime(int totalTime) {
            this.totalTime = totalTime;
        }

        public int getOffsetTime() {
            processChange();
            if (offsetTime >= totalTime) {
                offsetTime = 0;
            }
            return offsetTime;
        }

        private void processChange() {
            if (offsetTime >= totalTime / 2) {
                radius = defaultRadius;
                if (offsetTime >= totalTime / 2 + totalTime / 8 && offsetTime <= totalTime) {
                    if (alpha - 30 >= 0) {
                        alpha = alpha - 30;
                    }
                }

                if (offsetTime >= totalTime - totalTime / 4 && offsetTime <= totalTime) {
                    if (alpha + 30 < 255) {
                        alpha = alpha + 30;
                    }
                }
            } else {
                if (offsetTime >= totalTime / 4) {
                    radius -= mScaleFactor;
                } else {
                    radius += mScaleFactor;

                    alpha = 255;
                }
            }
            paint.setAlpha(alpha);
        }

        int times = 0;

        public void setOffsetTime(int offsetTime) {
            this.offsetTime = offsetTime;
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredHeight = measureHeight(heightMeasureSpec);
        int measuredWidth = measureWidth(widthMeasureSpec);

        setMeasuredDimension(measuredWidth, measuredHeight);
    }

    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 50;
        if (specMode == MeasureSpec.AT_MOST) {
            result = (int) maxRadius + 10;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }
    
  

    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int result = 60;
        if (specMode == MeasureSpec.AT_MOST) {
            int dis = distance / 2;
//            Log.i("MAGE", "距离dis:" + dis);
            result = (int) ((Math.sin(Math.PI * (double) dis / (double) distance) * dis) + maxRadius * 2);
//            Log.i("MAGE", "rsluit:" + result);
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        setVisibility(View.INVISIBLE);
    }
    
    @Override
    public void setVisibility(int visibility) {
        if (View.VISIBLE != visibility) {
            stopAnimation();
        } else {
            startAnimation();
        }
        super.setVisibility(visibility);
    }
    
}
